package redlib.backend.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import redlib.backend.dao.BioinformaticDocMapper;
import redlib.backend.dto.BioinformaticDocDTO;
import redlib.backend.dto.query.BioinformaticDocQueryDTO;
import redlib.backend.dto.query.KeywordQueryDTO;
import redlib.backend.model.BioinformaticDoc;
import redlib.backend.model.Page;
import redlib.backend.model.Token;
import redlib.backend.service.AdminService;
import redlib.backend.service.BioinformaticDocService;
import redlib.backend.service.utils.BioinformaticDocUtils;
import redlib.backend.utils.ElasticUtils;
import redlib.backend.utils.FormatUtils;
import redlib.backend.utils.PageUtils;
import redlib.backend.utils.ThreadContextHolder;
import redlib.backend.vo.BioinformaticDocVO;
import redlib.backend.vo.LuceneResultBookVO;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class BioinformaticDocServiceImpl implements BioinformaticDocService {
    @Autowired
    private BioinformaticDocMapper bioinformaticDocMapper;

    @Autowired
    private AdminService adminService;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /**
     * 分页获取生物信息学文献
     *
     * @param queryDTO 查询条件和分页信息
     * @return 带分页信息的文献数据列表
     */
    @Override
    public Page<BioinformaticDocVO> listByPage(BioinformaticDocQueryDTO queryDTO) {
        if (queryDTO == null) {
            queryDTO = new BioinformaticDocQueryDTO();
        }

        queryDTO.setTitle(FormatUtils.makeFuzzySearchTerm(queryDTO.getTitle()));
        Integer size = bioinformaticDocMapper.count(queryDTO);
        PageUtils pageUtils = new PageUtils(queryDTO.getCurrent(), queryDTO.getPageSize(), size);

        if (size == 0) {
            // 没有命中，则返回空数据。
            return pageUtils.getNullPage();
        }

        // 利用myBatis到数据库中查询数据，以分页的方式
        List<BioinformaticDoc> list = bioinformaticDocMapper.list(queryDTO, pageUtils.getOffset(), pageUtils.getLimit());

        // 提取list列表中的创建人字段，到一个Set集合中去
        Set<Integer> adminIds = list.stream().map(BioinformaticDoc::getCreatedBy).collect(Collectors.toSet());

        // 提取list列表中的更新人字段，追加到集合中去
        adminIds.addAll(list.stream().map(BioinformaticDoc::getCreatedBy).collect(Collectors.toSet()));

        // 获取id到人名的映射
        Map<Integer, String> nameMap = adminService.getNameMap(adminIds);

        List<BioinformaticDocVO> voList = new ArrayList<>();
        for (BioinformaticDoc bioinformaticDoc : list) {
            // BioinformaticDoc对象转VO对象
            BioinformaticDocVO vo = BioinformaticDocUtils.convertToVO(bioinformaticDoc, nameMap);
            voList.add(vo);
        }

        return new Page<>(pageUtils.getCurrent(), pageUtils.getPageSize(), pageUtils.getTotal(), voList);
    }


    /**
     * 新建文献
     *
     * @param bioinformaticDocDTO 文献输入对象
     * @return 文献编码
     */
    @Override
    public Integer addBioinformaticDoc(BioinformaticDocDTO bioinformaticDocDTO) {
        Token token = ThreadContextHolder.getToken();
        // 校验输入数据正确性
        BioinformaticDocUtils.validateBioinformaticDoc(bioinformaticDocDTO);
        // 创建实体对象，用以保存到数据库
        BioinformaticDoc bioinformaticDoc = new BioinformaticDoc();
        // 将输入的字段全部复制到实体对象中
        BeanUtils.copyProperties(bioinformaticDocDTO, bioinformaticDoc);
        bioinformaticDoc.setCreatedAt(new Date());
        bioinformaticDoc.setCreatedBy(token.getUserId());
        bioinformaticDoc.setUpdatedBy(token.getUserId());
        // 调用DAO方法保存到数据库表
        bioinformaticDocMapper.insert(bioinformaticDoc);

        // 新建elasticsearch索引
        List<BulkOperation> bulkList = new ArrayList<>();
        bulkList.add(ElasticUtils.addBulkBook(bioinformaticDoc.getId(), bioinformaticDoc.getUrl()));
        ElasticUtils.bulkAddDocument(elasticsearchClient, bulkList);
        return bioinformaticDoc.getId();
    }

    /**
     * 更新文献数据
     *
     * @param bioinformaticDocDTO 文献输入对象
     * @return 文献编码
     */
    @Override
    public Integer updateBioinformaticDoc(BioinformaticDocDTO bioinformaticDocDTO) {
        // 校验输入数据正确性
        Token token = ThreadContextHolder.getToken();
        BioinformaticDocUtils.validateBioinformaticDoc(bioinformaticDocDTO);
        Assert.notNull(bioinformaticDocDTO.getId(), "部门id不能为空");
        BioinformaticDoc bioinformaticDoc = bioinformaticDocMapper.selectByPrimaryKey(bioinformaticDocDTO.getId());
        Assert.notNull(bioinformaticDoc, "没有找到文献，Id为：" + bioinformaticDocDTO.getId());

        BeanUtils.copyProperties(bioinformaticDocDTO, bioinformaticDoc);
        bioinformaticDoc.setUpdatedBy(token.getUserId());
//        bioinformaticDoc.setUpdatedAt(new Date());
        bioinformaticDocMapper.updateByPrimaryKey(bioinformaticDoc);
        ElasticUtils.updateDocument(elasticsearchClient, bioinformaticDoc.getId(), bioinformaticDoc.getUrl());
        return bioinformaticDoc.getId();
    }

    /**
     * 根据编码列表，批量删除部门
     *
     * @param ids 编码列表
     */
    @Override
    public void deleteByCodes(List<Integer> ids) {
        Assert.notEmpty(ids, "文献id列表不能为空");
        bioinformaticDocMapper.deleteByCodes(ids);
        for (Integer id : ids) {
            ElasticUtils.deleteDocument(elasticsearchClient, id);
        }
    }

    @Override
    public Page<LuceneResultBookVO> search(KeywordQueryDTO queryDTO) {
        return ElasticUtils.query(elasticsearchClient, queryDTO);
    }
}
